<?php
/**
 * Created by PhpStrom
 * User: 郭碧云
 * Date: 2021/3/9 17:27
 * Desc:
 */

namespace App\Service;

use Exception;
use Illuminate\Support\Arr;

abstract class BaseService {
    /**
     * 模型
     * @var mixed
     */
    protected $model;
    /**
     * 模型命名空间,可以重新定义,定义后模型引用对应的空间
     * @var string
     */
    protected $modelNamespace = 'App\\Models';
    /**
     * 模型名称,通过"."语法,指定命名空间,变量中模型名称必须正确完整对应模型名称
     * @var string
     */
    protected $modelName = '';

    /**
     * 构造函数,优先使用参数构造初始化model,其次使用定义模型初始化
     * BaseService constructor.
     * @param string $modelName
     * @throws Exception
     */
    public function __construct($modelName = '') {
        if ($modelName) {
            $this->model = $this->createModel($modelName);
        } else {
            if ($this->modelName) {
                $this->model = $this->createModel($this->modelName);
            }
        }
    }

    /**
     * 新增数据方法
     * @param $data
     * @return mixed
     * @throws Exception
     */
    public function create($data) {
        $this->noModelException(__METHOD__);
        return $this->model->create($data)->toArray();
    }

    /**
     * 通过主键更新模型
     * @param $id
     * @param $data
     * @return mixed
     * @throws Exception
     */
    public function update($id, $data) {
        $this->noModelException(__METHOD__);
        $data=Arr::only($data,$this->model->getFillable()); //会过滤数据库存在而不在列表的字段
        return $this->model->where("id", $id)->update($data);
    }

    /**
     * 通过主键删除模型
     * @param $id int|array
     * @return int 删除数
     * @throws Exception
     */
    public function destroy($id) {
        $this->noModelException(__METHOD__);
        if (is_array($id)) {
            return $this->model::destroy($id);
        } else {
            return $this->model::destroy(intval($id));
        }
    }

    /**
     * 通过条件删除模型
     * @param $where
     * @return int 删除数量
     * @throws Exception
     */
    public function delete($where) {
        $this->noModelException(__METHOD__);
        return $this->model::where($where)->delete();
    }

    /**
     * 通过主键查找一个模型或者多个模型
     * @param $id int|array
     * @return null
     * @throws Exception
     */
    public function find($id) {
        $this->noModelException(__METHOD__);
        $obj = $this->model::find($id);
        if ($obj) {
            return $obj->toArray();
        } else {
            return null;
        }
    }

    /**
     * 根据条件获取首条数据
     * @param $where
     * @param array $orderBy 排序字段数组,如:['id'=>'asc','sort'=>'desc']
     * @return null
     * @throws Exception
     */
    public function first($where, $orderBy = []) {
        $this->noModelException(__METHOD__);
        $builder = $this->model::where($where);
        foreach ($orderBy as $k => $v) {
            $builder->orderBy($k, $v);
        }
        $obj = $builder->first();
        if ($obj) {
            return $obj->toArray();
        } else {
            return null;
        }

    }

    /**
     * 获取全部数据,无分页
     * @param string $field 查询字段
     * @param array $where 查询条件
     * @param string $groupBy 分组字段
     * @param array $orderBy 排序字段,排序方式
     * @return mixed
     * @throws Exception
     */
    public function getAll($field = '*', $where = [], $groupBy = '', $orderBy = []) {
        $this->noModelException(__METHOD__);
        $builder = $this->model::where($where);
        if ($groupBy) {
            if (!is_array($groupBy)) {
                $groupBy = explode(',', $groupBy);
            }
            $builder->groupBy($groupBy);
        }
        foreach ($orderBy as $k => $v) {
            $builder->orderBy($k, $v);
        }
        if (!is_array($field)) {
            $field = explode(',', $field);
        }
        return $builder->get($field)->toArray();
    }

    /**
     * @param string $field 查询字段
     * @param array $where 查询条件
     * @param string $groupBy 分组字段
     * @param array $orderBy 排序字段,排序方式
     * @param int $pageSize 分页大小
     * @return mixed
     * @throws Exception
     */
    public function getList($field = '*', $where = [], $groupBy = '', $orderBy = [], $pageSize = 20) {
        $this->noModelException(__METHOD__);
        $builder = $this->model::where($where);
        if ($groupBy) {
            if (!is_array($groupBy)) {
                $groupBy = explode(',', $groupBy);
            }
            $builder->groupBy($groupBy);
        }
        foreach ($orderBy as $k => $v) {
            $builder->orderBy($k, $v);
        }
        if (!is_array($field)) {
            $field = explode(',', $field);
        }
        return $builder->paginate($pageSize, $field)->toArray();
    }

    /**
     * 设置模型
     * @param $modelName
     * @return $this
     * @throws Exception
     */
    final public function setModel($modelName) {
        $this->model = $this->createModel($modelName);
        return $this;
    }

    private function createModel($modelName) {
        $pathArr = explode('.', $modelName);
        $pathArr = array_map(function ($item) {
            return ucwords($item);
        }, $pathArr);
        $class = implode('\\', $pathArr);
        $class = $this->modelNamespace . '\\' . $class;
        if (class_exists($class)) {
            return new $class();
        } else {
            throw new Exception("Error: '{$class}' not found");
        }
    }

    private function noModelException($method) {
        if (!$this->model) {
            throw new Exception("The action {$method} must not be accessed before the model of initialization");
        }
    }

    /**
     * 写入日志
     * @param $level
     * @param $module
     * @param $action
     * @param $message
     * @param null $data
     */
    public function log($level, $module, $action, $message, $data = null) {
        (new LogService())->log($level, $module, $action, $message, $data);
    }

}
